/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2021-06-10     liuduanfei   first version
 */

#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include "board.h"

#define DBG_TAG "uart"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#ifdef BSP_USING_UART

#if !defined(BSP_USING_UART1) && !defined(BSP_USING_UART2) && !defined(BSP_USING_UART3) && \
    !defined(BSP_USING_UART4)
#error "Please define at least one BSP_USING_UARTx"
#endif

enum
{
#ifdef BSP_USING_UART1
    UART1_INDEX,
#endif
#ifdef BSP_USING_UART2
    UART2_INDEX,
#endif
#ifdef BSP_USING_UART3
    UART3_INDEX,
#endif
#ifdef BSP_USING_UART4
    UART4_INDEX,
#endif
};

/* HC32 uart dirver class */
struct hc32_uart
{
    struct rt_serial_device serial;
    const char *name;
    M4_USART_TypeDef *Instance;
    stc_irq_regi_conf_t irq_conf;
};

#ifdef BSP_USING_UART1
    static void Usart1RxIrqCallback(void);
#endif

#ifdef BSP_USING_UART2
    static void Usart2RxIrqCallback(void);
#endif

#ifdef BSP_USING_UART3
    static void Usart3RxIrqCallback(void);
#endif

#ifdef BSP_USING_UART4
    static void Usart4RxIrqCallback(void);
#endif

struct hc32_uart uart_obj[] =
{
#ifdef BSP_USING_UART1
    {.name = "uart1", .Instance = M4_USART1, {.enIntSrc = INT_USART1_RI, .enIRQn= Int000_IRQn, .pfnCallback = Usart1RxIrqCallback}},
#endif
#ifdef BSP_USING_UART2
    {.name = "uart2", .Instance = M4_USART2, {.enIntSrc = INT_USART2_RI, .enIRQn= Int001_IRQn, .pfnCallback = Usart2RxIrqCallback}},
#endif
#ifdef BSP_USING_UART3
    {.name = "uart3", .Instance = M4_USART3, {.enIntSrc = INT_USART3_RI, .enIRQn= Int002_IRQn, .pfnCallback = Usart3RxIrqCallback}},
#endif
#ifdef BSP_USING_UART4
    {.name = "uart4", .Instance = M4_USART4, {.enIntSrc = INT_USART4_RI, .enIRQn= Int003_IRQn, .pfnCallback = Usart4RxIrqCallback}},
#endif
};

rt_uint32_t uart_num = (sizeof(uart_obj) / sizeof(uart_obj[0]));
#ifdef BSP_USING_UART1
static void Usart1RxIrqCallback(void)
{
    rt_hw_serial_isr(&uart_obj[UART1_INDEX].serial, RT_SERIAL_EVENT_RX_IND);
}
#endif

#ifdef BSP_USING_UART2
static void Usart2RxIrqCallback(void)
{
    rt_hw_serial_isr(&uart_obj[UART2_INDEX].serial, RT_SERIAL_EVENT_RX_IND);
}
#endif

#ifdef BSP_USING_UART3
static void Usart3RxIrqCallback(void)
{
    rt_hw_serial_isr(&uart_obj[UART3_INDEX].serial, RT_SERIAL_EVENT_RX_IND);
}
#endif

#ifdef BSP_USING_UART4
static void Usart4RxIrqCallback(void)
{
    rt_hw_serial_isr(&uart_obj[UART4_INDEX].serial, RT_SERIAL_EVENT_RX_IND);
}
#endif

static rt_err_t hc32_configure(struct rt_serial_device *serial,
                                struct serial_configure *cfg)
{
    struct hc32_uart *uart = RT_NULL;
    const stc_usart_uart_init_t stcInitCfg = {
        UsartIntClkCkNoOutput,
        UsartClkDiv_1,
        UsartDataBits8,
        UsartDataLsbFirst,
        UsartOneStopBit,
        UsartParityNone,
        UsartSampleBit8,
        UsartStartBitFallEdge,
        UsartRtsEnable,
    };

    uart = rt_container_of(serial, struct hc32_uart, serial);
    
    /* clk, pin ... */
    usart_ll_init(uart->Instance);
    
    USART_UART_Init(uart->Instance, &stcInitCfg);
    
    /* Set baudrate */
    USART_SetBaudrate(uart->Instance, 115200);
    
    /* Enable RX && TX function */
    USART_FuncCmd(uart->Instance, UsartRx, Enable);
    USART_FuncCmd(uart->Instance, UsartTx, Enable);
    
    return RT_EOK;
}

static rt_err_t hc32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct hc32_uart *uart = RT_NULL;
    stc_irq_regi_conf_t stcIrqRegiCfg;
    
    uart = rt_container_of(serial, struct hc32_uart, serial);    
    switch (cmd)
    {
    /* disable interrupt */
    case RT_DEVICE_CTRL_CLR_INT:
        break;
    /* enable interrupt */
    case RT_DEVICE_CTRL_SET_INT:
        /* Set USART RX IRQ */
        stcIrqRegiCfg.enIRQn = uart->irq_conf.enIRQn;
        stcIrqRegiCfg.pfnCallback = uart->irq_conf.pfnCallback;
        stcIrqRegiCfg.enIntSrc = uart->irq_conf.enIntSrc;
        enIrqRegistration(&stcIrqRegiCfg);
        NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
        NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
        NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

        USART_FuncCmd(uart->Instance, UsartRxInt, Enable);

        break;
    }
    return RT_EOK;
}

static int hc32_putc(struct rt_serial_device *serial, char c)
{
    struct hc32_uart *uart = RT_NULL;
    uart = rt_container_of(serial, struct hc32_uart, serial);

    /* Warit Tx data register empty */
    while (Reset == USART_GetStatus(uart->Instance, UsartTxEmpty))
    {
    }

    USART_SendData(uart->Instance, c);

    return 1;
}

static int hc32_getc(struct rt_serial_device *serial)
{
    int ch= -1;
    struct hc32_uart *uart = RT_NULL;
    uart = rt_container_of(serial, struct hc32_uart, serial);

    /* Warit Rx data register no empty */    
    if (USART_GetStatus(uart->Instance, UsartRxNoEmpty))
    {
        ch = USART_RecData(uart->Instance);
    }

    return ch;
}

static rt_size_t hc32_dma_transmit(struct rt_serial_device *serial,
                                rt_uint8_t *buf,
                                rt_size_t size,
                                int direction)
{

    return 0;
}



#if defined(BSP_USING_UART1)

#endif

static const struct rt_uart_ops hc32_uart_ops =
{
    .configure = hc32_configure,
    .control = hc32_control,
    .putc = hc32_putc,
    .getc = hc32_getc,
    .dma_transmit = hc32_dma_transmit
};

int rt_hw_uart_init(void)
{
    rt_uint32_t i = 0;
    rt_err_t result = RT_EOK;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;

    for (i = 0; i < uart_num; i++)
    {
        /* init serial */
        uart_obj[i].serial.ops    = &hc32_uart_ops;
        uart_obj[i].serial.config = config;

        /* register UART device */
        result = rt_hw_serial_register(&uart_obj[i].serial,
                                       uart_obj[i].name,
                                       (RT_DEVICE_FLAG_RDWR   |
                                        RT_DEVICE_FLAG_INT_RX |
                                        RT_DEVICE_FLAG_INT_TX
                                       ),
                                       &uart_obj[i]);
        RT_ASSERT(result == RT_EOK);
    }

    return result;
}

#endif /* BSP_USING_UART */
